<?php
	/**
	 * DataEngine class
	 *
	 */
	class DataEngine
	{	
		/**
		 * Specific database engine
		 *
		 * @var string
		 */
		public $engine;
		
		/**
		* default class constructor
		*
		* @param array $db_config database configuration array
		*/
		function __construct($db_config)
		{
			Logger::Debug("Data engine constructor started");
			if (isset($db_config))
			{
				include_once('engines'.SEPARATOR.$db_config['driver'].'.php');
				$engine_type = $db_config['driver'].'Engine';
				$this->engine = new $engine_type();
				
				Logger::Debug("Connect started");
				$this->engine->Connect($db_config);
				Logger::Debug("Connect ended");
			}
			else 
			{
				throwError(ERROR, 'Wrong Engine Name');
			}
			Logger::Debug("Data engine construct ended");
		}
		
		
		//BASE FUNCTIONS
		
		/**
		* Execute stored procedure or sql statement
		*
		* @param object $object current object
		* @param array $parameters: parameters for the stored procedure
		* @param string $stored_procedure stored procedure name (default: null)
		* @param string $sql sql query (default: null)
		* @return array $result DB query result
		*/
		function Execute($object, $parameters, $stored_procedure = null, $sql = null)
		{
			$parameters = $this->ObjectFieldsUnmap($parameters, $object);
			if (($object->config->use_stored_procedures) && (!isset($sql)))
			{
				if ($stored_procedure == null)
				{
					throwError(DB_ERROR,'Stored procedure not set!');
				}
				else 
				{
					$result = $this->engine->ExecuteStoredProcedure($stored_procedure, $parameters, $object->db_structure);			
					if ($result != false)
					{
						if (count($result) == 1)
						{
							unset($this->fieldName);
							$result = $this->ObjectFieldsMap($result[0], $object);
							unset($this->fieldName);		
						}
						else 
						{
							$i = 0;
							unset($this->fieldName);
							foreach ($result as $key=>$value)
							{
								$result[$i] = $this->ObjectFieldsMap($result[$i], $object);	
								$i++;
							}
							unset($this->fieldName);
							unset($i);
						}
					}
				}
			}
			else 
			{
				$result = $this->engine->ExecuteSQL($sql);
			}
			return $result;
		}
		
		/**
		* Gets object by primary key
		*
		* @param $object current object
		* @param $id: primary key value
		* @param string $stored_procedure stored procedure name (default: null)
		* @return array $result DB query result (one record)
		*/
		function Get($object, $id, $stored_procedure = null)
		{
			if ($object->config->use_stored_procedures)
			{
				if ($stored_procedure == null)
				{
					$stored_procedure_name = $object->stored_procedure_get;
				}
				else 
				{
					$stored_procedure_name = $stored_procedure;
				}
				if ($stored_procedure_name)
				{
					$params = array();
					$params[$object->GetPrimaryKey()] = $id;
					$result = $this->engine->ExecuteStoredProcedureObject($stored_procedure_name, $params, $object->db_structure);			
					unset($this->fieldName);
					$result = $this->ObjectFieldsMap($result, $object);
					unset($this->fieldName);
				}
				else 
				{
					throwError(DB_ERROR,'Stored procedure not set!');
				}
			}
			else 
			{
				//TODO
				//Write code for query generator
			}
			return $result;
		}
		
		/**
		* Updates or inserts given object
		*
		* @param array $data data to be saved
		* @param object $object current object
		* @param $stored_procedure stored procedure name (default: 0)
		* @return boolean $result update result
		*/
		function Update($data, $object, $stored_procedure = null, &$out_parameters = null)
		{
			$params = $this->ObjectFieldsUnmap($data, $object);
			if ($object->config->use_stored_procedures)
			{
				if (isset($stored_procedure))
				{
					$stored_procedure_name = $stored_procedure;
				}
				else 
				{
					if ($params[$object->GetPrimaryKey()] == null)
					{
						$stored_procedure_name = $object->stored_procedure_insert;
					}
					else 
					{
						$stored_procedure_name = $object->stored_procedure_update;
					}
				}
				$result = $this->engine->ExecuteStoredProcedure($stored_procedure_name, $params, $object->db_structure, $out_parameters);
			}
			else
			{
				//TODO
				//Write code for query generator		
			}
			
		 	return $result;
		}
		
		/**
		* Deletes object by id
		*
		* @param $id primary key
		* @param object $object current object
		* @param $stored_procedure stored procedure name (default: 0)
		* @return boolean $result delete result
		*/
		function Delete($id, $object, $stored_procedure = null)
		{
			if ($object->config->use_stored_procedures)
			{
				if ($stored_procedure == null)
				{
					$stored_procedure_name = $object->stored_procedure_delete;
				}
				else 
				{
					$stored_procedure_name = $stored_procedure;
				}
				$params = array();
				$params[$object->GetPrimaryKey()] = $id;
				$result = $this->engine->ExecuteStoredProcedure($stored_procedure_name, $params, $object->db_structure);			
			}
			else 
			{
				//TODO
				//Write code for query generator
			}
			return $result;
		}
		
		/**
		* Gets records from DB
		*
		* @param object $object: current object
		* @param string $parameters parameters
		* @param string $stored_procedure stored procedure name (default: 0)
		* @return array $result records list
		*/
		function ListItems($object, $parameters, $stored_procedure = null, &$out_parameters=null)
		{
			$parameters = $this->ObjectFieldsUnmap($parameters, $object);
			if ($object->config->use_stored_procedures)
			{
				if ($stored_procedure == null)
				{
					$stored_procedure_name = $object->stored_procedure_list;
				}
				else 
				{
					$stored_procedure_name = $stored_procedure;
				}
				if ($stored_procedure_name)
				{
					$result = $this->engine->ExecuteStoredProcedureObjects($stored_procedure_name, $parameters, $object->db_structure, &$out_parameters);			
					
					//loops through all rows
					Logger::Debug("Mapping started");
					$i = 0;
					unset($this->fieldName);
					
					$result_count = count($result);
					
					foreach ($result as $key => $value)
					{
						$result[$i] = $this->ObjectFieldsMap($result[$i], $object, $result_count);	
						++$i;
					}
					unset($this->fieldName);
					unset($i);
					Logger::Debug("Mapping ended");
				}
				else 
				{
					throwError(DB_ERROR,'Stored procedure not set!');
				}
			}
			else 
			{
				//TODO
				//Write code for query generator
			}
			return $result;
		}
		
		/**
		* Gets paged records from DB
		*
		* @param object $object: current object
		* @param string $parameters parameters
		* @param string $stored_procedure stored procedure name (default: 0)
		* @return array $result records list
		*/		
		function ListItemsPaged($object, $parameters, $stored_procedure = null, &$out_parameters=null)
		{
			if ($object->config->use_stored_procedures)
			{
				if ($stored_procedure == null)
				{
					$stored_procedure_name = $object->stored_procedure_list_paged;
				}
				else 
				{
					$stored_procedure_name = $stored_procedure;
				}
				if ($stored_procedure_name)
				{
					$result = $this->engine->ExecuteStoredProcedureObjects($stored_procedure_name, $parameters, $object->db_structure, &$out_parameters);			
					$i = 0;
					foreach ($result as $key=>$value)
					{
						$result[$i] = $this->ObjectFieldsMap($result[$i], $object);	
						$i++;
					}
					unset($this->fieldName);
					unset($i);
				}
				else 
				{
					throwError(DB_ERROR,'Stored procedure not set!');
				}
			}
			else 
			{
				//TODO
				//Write code for query generator
			}
			return $result;
		}


		//PARAMETERS MAPPING
		
		/**
		* Maps object names into database fields
		*
		* @param array $data names' array 
		* @param object $object object to parse
		* @return $result_array array with mapped fields
		*/
		function ObjectFieldsMap($data, $object, $result_count = 1)
		{
			if (!empty($data))
			{	
				//loops through all fields
				$j = 0;	
				foreach ($data as $key=>$value)
				{
					//if map 1 result
					if ($result_count == 1)
					{
						if (!($object->db_structure[$key]['type'] == 'file'))
						{
							$value = $this->ParseParametersFromDB($value);
						}
					}
					//if map several results
					else
					{
						$value = $this->ParseParametersFromDB($value);
					}
					
					if (!isset($this->fieldName[$j]))
					{
						$this->fieldName[$j] = $this->GetObjectFieldName($object, $key);
					}

					$result_array[$this->fieldName[$j]] = $value;
					//$result_array[$this->GetObjectFieldName($object, $key)] = $value;
					++$j;
				}
			}
			return $result_array;
		}
		
		/**
		* Unmaps database fields into object names
		*
		* @param array $data names' array 
		* @param object $object object to parse
		* @return $params array with unmapped fields
		*/
		function ObjectFieldsUnmap($data, $object)
		{
			if (!empty($data))
			{
				foreach ($data as $key=>&$value) 
				{
					if (!($object->db_structure[$key][type] == 'file'))
					{
						$value = strip_tags($value, "<div>, <span>, <table>, <tr>, <td>, <th>, <tbody>, <font>, <b>, <strong>, <i>, <u>, <em>, <strike>, <p>, <pre>, <br>, <a>, <img>, <ul>, <ol>, <li>, <sup>, <sub>");
						$value = addslashes($value);
					}
					if (($key=='page_offset') || ($key=='page_number') || ($key=='sort_by') || ($key=='sort_method')) 
					{
						$params[$key] = $value;
					}
					else 
					{
						$params[$this->GetObjectField($object, $key)] = $value;
					}
				}
			}
			unset($value);		
			return $params;
		}
		
		/**
		* Searches object name by database field
		*
		* @param object $object object to parse
		* @param string $field_to_search database field to search
		* @return string found name or null
		*/
		function GetObjectFieldName($object, $field_to_search)
		{
			foreach ($object->db_structure as $key=>$value) 
			{
				if ($field_to_search == $value['name'])
				{
					return $key;
				}	
			}
		}
		
		/**
		* Searches database field by object name
		*
		* @param object $object object to parse
		* @param string $name_to_search object name to search
		* @return string found database field or null
		*/
		function GetObjectField($object, $name_to_search)
		{
			$result = null;
			foreach ($object->db_structure as $key=>$value) 
			{
				if ($name_to_search == $key)
				{
					$result = $value['name'];
					break;
				}	
			}
			return $result;
		}
		
		/**
		 * Parses fields, which came from the database
		 *
		 * @param string $value value to parse
		 * @return value
		 */
		function ParseParametersFromDB($value)
		{
			$value = stripslashes($value);
			$value = str_replace("'", '"', $value);
			return $value;
		}

	
		//SQL SCHEME FUNCTIONS
		
		/**
		 * Gets DB structure
		 *
		 * @return array
		 */
		function GetSchema(array $options)
		{
			$result = $this->engine->GetSchema($options);
			return $result;
		}	

		/**
		 * Gets DB tables list
		 *
		 * @return array
		 */
		function GetTables(array $options)
		{
			$result = $this->engine->GetTables($options);
			return $result;
		}	
		
		/**
		 * Gets SQL object code
		 *
		 * @param string $obj_name name of the object
		 * @return array SQL-code of the database objectsn by the line
		 */
		function GetSqlObjectCode($obj_name)
		{
			return  $this->engine->GetSqlObjectCode($obj_name);
		}
		
		/**
		 * Gets SQL object names
		 *
		 * @param string $obj_name part of the name of the SQL-object to pick out only necessary objects
		 * @return array SQL-object names of the database
		 */
		function GetSqlObjectNames($obj_name='')
		{
			return  $this->engine->GetSqlObjectNames($obj_name);
		}
	}
?>